﻿#include "FITKGraphObjectNodeLoadBase.h"

// VTK
#include <vtkDoubleArray.h>
#include <vtkUnstructuredGrid.h>
#include <vtkProperty.h>
#include <vtkPointData.h>
#include <vtkSelectionNode.h>
#include <vtkIntArray.h>

// Graph
#include "FITK_Interface/FITKVTKAlgorithm/FITKGraphActor.h"

// Global data
#include "FITK_Kernel/FITKCore/FITKDataRepo.h"

// Data
#include "FITK_Interface/FITKInterfacePhysics/FITKBoundaryLocation.h"
#include "FITK_Interface/FITKInterfaceStructural/FITKAbaRefPoint.h"
#include "FITK_Kernel/FITKCore/FITKVec3D.h"
#include "FITK_Interface/FITKInterfaceStructural/FITKAbaAssembly.h"
#include "FITK_Interface/FITKInterfaceModel/FITKModelSet.h"
#include "FITK_Component/FITKAbaqusData/FITKAbaModelInfo.h"

#include "FITK_Component/FITKAbaqusData/FITKAbaqusPart.h"
#include "FITK_Interface/FITKInterfaceModel/FITKAssInstance.h"
#include "FITK_Interface/FITKInterfaceStructural/FITKAbaMeshPartVTK.h"

// Data manager
#include "FITKSetSurfPolyProvider.h"

namespace Exchange
{
    FITKGraphObjectNodeLoadBase::FITKGraphObjectNodeLoadBase(AbaqusData::FITKDataCase* caseData, Interface::FITKBoundaryLocation* loadData, int stepId)
        : FITKGraphObjectLoadBase(caseData, loadData, stepId)
    {
        if (!loadData)
        {
            return;
        }

        // Initialize the type of extractor.
        m_selectNode->SetFieldType(vtkSelectionNode::SelectionField::POINT);
    }

    bool FITKGraphObjectNodeLoadBase::generatePolyData(Interface::FITKBoundaryLocation* load)
    {
        // Get set detail information.
        AbaqusData::AbaModelInfo requiredData = AbaqusData::FITKAbaModelInfo::GetModelInfo(load);

        // The set must be not a nullptr.
        if (!requiredData._set)
        {
            m_setId = -1;
            return false;
        }

        // Save the set id.
        m_setId = requiredData._set->getDataObjectID();

        // Save the sub set ids.
        m_subSetIds = FITKSetSurfPolyProvider::GetCompSetSubSetIds(m_setId);

        // Try to get the parent instance id.
        int instanceId = -1;
        if (requiredData._instance)
        {
            instanceId = requiredData._instance->getDataObjectID();
        }

        // Generate the set poly data recursively.
        generateSetPolyData(requiredData._set, instanceId);

        return true;
    }

    void FITKGraphObjectNodeLoadBase::generateSetPolyData(Interface::FITKModelSet* set, int instanceId)
    {
        // Get the sub-set number.
        int nSubSet = set->getDataCount();
        if (nSubSet == 0)
        {
            // Get set type.
            Interface::FITKModelEnum::FITKModelSetType type = set->getModelSetType();

            // The node load must be set on a node set.
            if (type != Interface::FITKModelEnum::FITKModelSetType::FMSNode)
            {
                return;
            }

            // Try to get parent instance id, if the parent instance is nullptr,
            // the set's parent is probably a part, so use the instance id from 
            // the load model info.
            AbaqusData::AbaModelInfo requiredData = AbaqusData::FITKAbaModelInfo::GetModelInfo(set);
            if (requiredData._instance)
            {
                instanceId = requiredData._instance->getDataObjectID();
            }

            // The set for load must has a model data.
            bool isValid = false;

            // Try to get the model data as an instance.
            Interface::FITKAbstractAssInstance* instanceParent = Core::FITKDataRepo::getInstance()->getTDataByID<Interface::FITKAbstractAssInstance>(set->getAbsModelID());
            if (instanceParent)
            {
                instanceId = set->getAbsModelID();
            }

            // Assembly comp-set or Assembly-instance set.
            if (!isValid && requiredData._assembly && (instanceId != -1) && requiredData._set)  //  && requiredData._instance
            {
                // Get the instance data.
                Interface::FITKAbstractAssInstance* instance = Core::FITKDataRepo::getInstance()->getTDataByID<Interface::FITKAbstractAssInstance>(instanceId);
                if (instance)
                {
                    // NSet load.
                    isValid = true;
                    generatePointsWithInstanceSet(requiredData._set, instance);
                }
            }

            // Assembly node set ( Or a ref-node ).
            if (!isValid && requiredData._assembly && requiredData._set)
            {
                isValid = true;
                generatePointsWithAssemblySet(requiredData._set, requiredData._assembly);
            }

            // Instance-part set.
            if (!isValid && (instanceId != -1) && requiredData._part && requiredData._set) //  && requiredData._instance
            {
                // Get the instance data.
                Interface::FITKAbstractAssInstance* instance = Core::FITKDataRepo::getInstance()->getTDataByID<Interface::FITKAbstractAssInstance>(instanceId);
                if (instance)
                {
                    // NSet load.
                    isValid = true;
                    generatePointsWithInstanceSet(requiredData._set, instance);
                }
            }
        }
        else
        {
            // Generate sub-set poly data.
            for (int i = 0; i < nSubSet; i++)
            {
                generateSetPolyData(set->getDataByIndex(i), instanceId);
            }   
        }
    }

    void FITKGraphObjectNodeLoadBase::generatePointsWithInstanceSet(Interface::FITKModelSet* set, Interface::FITKAbstractAssInstance* instance)
    {
        if (!set || !instance)
        {
            return;
        }

        // Get or generate set data.
        int modelId = -1;
        vtkUnstructuredGrid* setGrid = FITKSetSurfPolyProvider::getInstance()->getSetGridData(set->getDataObjectID(), modelId, instance->getDataObjectID());
        if (!setGrid)
        {
            return;
        }

        // Get the model id array in point data.
        vtkIntArray* modelIdArray = vtkIntArray::SafeDownCast(setGrid->GetPointData()->GetArray(FITKSetSurfPolyProvider::getInstance()->getModelIdsArrayName()));
        if (modelIdArray)
        {
            for (int i = 0; i < setGrid->GetNumberOfPoints(); i++)
            {
                // Insert point and cell and value to poly data.
                int modelId = modelIdArray->GetValue(i);
                insertNextPoint(setGrid->GetPoint(i), modelId);
            }
        }
        else
        {
            for (int i = 0; i < setGrid->GetNumberOfPoints(); i++)
            {
                // Insert point and cell and value to poly data.
                insertNextPoint(setGrid->GetPoint(i), -1);
            }
        }

        if (!m_modelIds.contains(modelId) && modelId != -1)
        {
            m_modelIds.push_back(modelId);
        }
    }

    void FITKGraphObjectNodeLoadBase::generatePointsWithAssemblySet(Interface::FITKModelSet* set, Interface::FITKAbaAssembly* assembly)
    {
        if (!set || !assembly)
        {
            return;
        }

        // Get or generate set data.
        int modelId = -1;
        vtkUnstructuredGrid* setGrid = FITKSetSurfPolyProvider::getInstance()->getSetGridData(set->getDataObjectID(), modelId);
        if (!setGrid)
        {
            return;
        }

        // Get the model id array.
        vtkIntArray* modelIdArray = vtkIntArray::SafeDownCast(setGrid->GetPointData()->GetArray(FITKSetSurfPolyProvider::getInstance()->getModelIdsArrayName()));
        if (modelIdArray)
        {
            for (int i = 0; i < setGrid->GetNumberOfPoints(); i++)
            {
                // Insert point and cell and value to poly data.
                int modelId = modelIdArray->GetValue(i);
                insertNextPoint(setGrid->GetPoint(i), modelId);
            }
        }
        else
        {
            for (int i = 0; i < setGrid->GetNumberOfPoints(); i++)
            {
                // Insert point and cell and value to poly data.
                insertNextPoint(setGrid->GetPoint(i), -1);
            }
        }

        if (!m_modelIds.contains(modelId) && modelId != -1)
        {
            m_modelIds.push_back(modelId);
        }
    }
}   // namespace Exchange